using System;
using System.Windows.Forms;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Net;
using System.Xml;
using System.Collections;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;


namespace edu.stanford.hci.ideas
{
    public enum EVENT_TYPE
    {
        SENSOR_UPDATE, RANGE_MODE_CHANGED, SELECTION, DRAW_STROKE, MOVE_STROKES, PEN_GRABBED, PEN_RELEASED, START_APP, IDLE, SHOW_NOTEBOOK_PAGES, ERASE_STROKES, CAMERA_EVENT, SYNC_BEGIN, SYNC_END
    }

    public enum PEN_STATUS
    {
        IN_HAND, IN_TRAY
    }

    public class UsageLogger
    {
        static private String EVENT_TABLE_HEADER = "SessionID, EventID, Ticks, TimeString, TimeSinceInit, ElapsedTime, EventType, PenStatus, RangeMode, Ranger1, Ranger2, Ranger3, Ranger4, AudioLevel, NumFaces, FaceX0, FaceY0, FaceR0, FaceX1, FaceY1, FaceR1, FaceX2, FaceY2, FaceR2, FaceX3, FaceY3, FaceR3, NumBlobs, BlobX0, BlobY0, BlobR0, BlobX1, BlobY1, BlobR1, BlobX2, BlobY2, BlobR2, BlobX3, BlobY3, BlobR3, BlobX4, BlobY4, BlobR4, BlobX5, BlobY5, BlobR5, BlobX6, BlobY6, BlobR6, BlobX7, BlobY7, BlobR7, BlobX8, BlobY8, BlobR8, BlobX9, BlobY9, BlobR9";

        static private String[] Event_Names =
            {
                "SENSOR_UPDATE", 
                "RANGE_MODE_CHANGED", 
                "SELECTION",
                "DRAW_STROKE", 
                "MOVE_STROKES", 
                "PEN_GRABBED",
                "PEN_RELEASED",
                "START_APP",
                "IDLE",
                "SHOW_NOTEBOOK_PAGES",
                "ERASE_STROKES",
                "CAMERA_EVENT",
	        "BEGIN_SYNC",
		"END_SYNC"
            };

        static private String[] PenStatus_Name = { "IN_HAND", "IN_TRAY" };

        [DllImport("user32.dll")]
		static extern bool BringWindowToTop(IntPtr hwnd);

		[DllImport("user32.dll", SetLastError=true)]
		static extern int DestroyIcon(IntPtr hIcon);

		[DllImport("user32.dll", EntryPoint="FindWindow")]
		static extern IntPtr FindWindowWin32(string className, string windowName);

		[DllImport("user32.dll")]
		static extern int GetWindowLong(IntPtr hwnd, int index);

		[DllImportAttribute("user32.dll")]
		static extern IntPtr LoadImage(int hInst, string name, int type, int cx, int cy, int load);

		[DllImport("user32.dll")]
		static extern bool SetForegroundWindow(IntPtr hWnd);

		[DllImport("user32.dll")]
		static extern int SetWindowLong(IntPtr hwnd, int index,	int dwNewLong);

		[DllImport("shell32.dll")]
		static extern void SHChangeNotify(int wEventId, int uFlags, [MarshalAs(UnmanagedType.LPWStr)] string dwItem1, IntPtr dwItem2);

		[DllImport("shell32.dll")]
		static extern bool SHObjectProperties(IntPtr hwnd, int type, [MarshalAs(UnmanagedType.LPWStr)] string lpObject, [MarshalAs(UnmanagedType.LPWStr)] string lpPage);

		[DllImport("user32.dll")]
		static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);

        // singleton pattern

        private UsageLogger(String eventTableFilename) {
            _event_sw = openStream(eventTableFilename, EVENT_TABLE_HEADER, true);
        }

        //public void Connect(String filename)
        //{
            //FileStream eventFS = new FileStream(filename, FileMode.Append, FileAccess.Write);
            //_event_sw = new StreamWriter(eventFS);
        //}

        StreamWriter openStream(String filename, String header, bool initEventID)
        {
            bool writeHeader = false;
            //  char fullPath[255];
            // sprintf (fullPath, "%s/%s", _program_path, filename);
            FileStream fsInput = null;
            try
            {
                fsInput = new FileStream(filename, FileMode.Open, FileAccess.Read);
            }
            catch (FileNotFoundException)
            {
                _current_event_id = 0;
                _session_id = 0;
                writeHeader = true;
            }
            if (!writeHeader)
            {
                StreamReader srInput = new StreamReader(fsInput);
                if (initEventID)
                {
                    // find out event id
                    // get length of file:
                    fsInput.Seek(0, SeekOrigin.End);
                    long length = fsInput.Position;

                    //cerr << "length: " << length << endl;
                    bool foundNonWhitespace = false;
                    for (long i = length - 1; i > 0; i--)
                    {
                        fsInput.Seek(i, SeekOrigin.Begin);
                        byte[] arr = { 0 };
                        fsInput.Read(arr, 0, 1);
                        int c = arr[0];
                        long p = fsInput.Position;
                        if (foundNonWhitespace && (c == 13 || c == 10))
                        {
                            String line = srInput.ReadLine();
                            string[] columns = line.Split(',');
                            _current_event_id = int.Parse(columns[1]);
                            _session_id = int.Parse(columns[0]);
                            Console.WriteLine("Event ID found: " + _current_event_id);
                            _session_id++;
                            _current_event_id++;
                            break;
                        }
                        else if (c != 13 && c != 10)
                        {
                            foundNonWhitespace = true;
                        }
                    }
                }
                srInput.Close();
            }
            FileStream fsAppend = new FileStream(filename, FileMode.Append, FileAccess.Write);
            StreamWriter sw = new StreamWriter(fsAppend);

            if (writeHeader)
            {
                sw.WriteLine(EVENT_TABLE_HEADER);
            }
            return sw;
        }

        static public UsageLogger GetInstance()
        {
            if (_instance != null)
            {
                return _instance;
            }
            else
            {
                _instance = new UsageLogger("event_table.csv");
                return _instance;
            }
        }

        public static void logEvent(DateTime timeStamp, EVENT_TYPE eventType, ZONES range, PEN_STATUS penStatus, RangerEvent rangerEvent, AudioEvent audioEvent, CameraEvent cameraEvent)
        {
            UsageLogger foo = GetInstance();
            foo.imp_write_event(timeStamp, eventType, range, penStatus, rangerEvent, audioEvent, cameraEvent);
        }

        private void imp_write_event(DateTime timeStamp, EVENT_TYPE eventType, ZONES range, PEN_STATUS penStatus, RangerEvent rangerEvent, AudioEvent audioEvent, CameraEvent cameraEvent)
        {
            //String eventTypeStr = "Unknown";
            String eventTypeStr = Event_Names[(int)eventType];
            
            //String rangeStr = "Unknown";
            String rangeStr = rangerEvent.getZoneNames(range);
           
           // String penStr = "Unknown";
            String penStr = PenStatus_Name[(int)penStatus];
            //switch (penStatus)
            //{
            //    case PEN_STATUS.IN_HAND:
            //        penStr = "InHand";
            //        break;
            //    case PEN_STATUS.IN_TRAY:
            //        penStr = "InTray";
            //        break;
            //    default:
            //        break;
            //};

            // if _end_time is not null, there is an idle event.
            // xxx dla below is commented out for now. Support for "idle" events may be added back later.

            /*
            if (_end_time != null)
            {
                TimeSpan idleSpan;

                if (_duration_event)
                {
                    // time between end of previous event and start of
                    // current event:
                    idleSpan = _start_time - _end_time;
                }
                else
                {
                    // time between end of previous event and NOW:
                    idleSpan = timeStamp - _end_time;
                }
                double idleElapsedSecs = idleSpan.TotalSeconds;
                logIdleEvent(_end_time, idleElapsedSecs);
            }
            */
            double elapsedTime = 0.0;
            if (eventType != EVENT_TYPE.SENSOR_UPDATE)
            {
                _end_time = timeStamp;

                if (_duration_event)
                {
                    TimeSpan eventSpan = _end_time - _start_time;
                    elapsedTime = eventSpan.TotalSeconds;
                }
            }

            float[] rangerValues = rangerEvent.getRawValues();
  
            double audioLevel = audioEvent.getLevel() * 100;
            
            DateTime timeToLog;
            if (_duration_event)
            {
                timeToLog = _start_time;
            } else
            {
                timeToLog = timeStamp;
            }
            double timeSinceInit = (timeToLog - _init_time).TotalSeconds;
            _event_sw.Write(_session_id + ", ");
            _event_sw.Write( _current_event_id + ", ");
            _event_sw.Write(timeToLog.Ticks + ", ");
            _event_sw.Write(timeToLog.ToShortDateString() + " "+timeToLog.Hour.ToString("00") + ":" + timeToLog.Minute.ToString("00") + ":" + timeToLog.Second.ToString("00") + ", ");
            _event_sw.Write(timeSinceInit + ", " + elapsedTime + ", ");
            _event_sw.Write(eventTypeStr + ", ");
            _event_sw.Write(penStr+ ", " );
            _event_sw.Write(rangeStr + ", " + rangerValues[0] + ", " + rangerValues[1] + ", " + rangerValues[2] + ", " + rangerValues[3] + ", ");
            _event_sw.Write(audioLevel + ", ");
            int numFaces = cameraEvent.getNumFaces();
            _event_sw.Write(numFaces+ ", ");
            for (int i = 0; i < numFaces;i++){
                
                 _event_sw.Write(cameraEvent.getFaceX(i)+ ", "+ cameraEvent.getFaceY(i)+", "+cameraEvent.getFaceRadius(i)+", ");
            }
            for (int i = numFaces; i < cameraEvent.getMaxFaces(); i++)
            {
                _event_sw.Write("-1, -1, -1, ");
            }
            int numBlobs = cameraEvent.getNumBlobs();
            _event_sw.Write(numBlobs + ", ");
            for (int i = 0; i < numBlobs; i++)
            {
                _event_sw.Write(cameraEvent.getBlobX(i)+ ", "+ cameraEvent.getBlobY(i)+", "+cameraEvent.getBlobRadius(i)+", ");
            }
            for (int i = numBlobs; i < cameraEvent.getMaxBlobs(); i++)
            {
                _event_sw.Write("-1, -1, -1, ");
            }
            _event_sw.Write("\n");
            _event_sw.Flush();
            _current_event_id++;
            if (eventType != EVENT_TYPE.SENSOR_UPDATE)
            {
                //_start_time = DateTime.Now;
                _duration_event = false;
            }
        }

        public void logIdleEvent(DateTime timeStamp, double elapsedTime)
        {
            _event_sw.WriteLine(_session_id + ", " + _current_event_id + ", " + timeStamp.Ticks + ", " + timeStamp.ToShortDateString() + " " + timeStamp.Hour.ToString("00") + ":" + timeStamp.Minute.ToString("00") + ":" + timeStamp.Second.ToString("00") + ", " + elapsedTime + ", " + "Idle" + ", " + "N/A" + ", " + "N/A" + ", " + "N/A" + ", " + "N/A" + ", " + "N/A" + ", " + "N/A" + ", " + "N/A");
            _event_sw.Flush();
            _current_event_id++;
        }

        public void beginTiming()
        {
            _start_time = DateTime.Now;
            _duration_event = true;
        }

        public static void captureImage(Control c, DateTime timestamp)
        {
            Bitmap b = Emxx.Capture.Control(c, false, false);
            long ticks = timestamp.Ticks;
            String filename = ticks + "_CAP.jpg";
            b.Save(filename, ImageFormat.Jpeg);
        }

    static private UsageLogger _instance = null;

        private StreamWriter _event_sw = null;
        private DateTime _start_time = DateTime.Now;
        private bool _duration_event;
        private DateTime _end_time = DateTime.Now;
        private int _session_id;
        private int _current_event_id;
        static private DateTime _init_time = DateTime.Now;
 
    }
}